test(e2e): guard destructive spend-log truncate behind an explicit opt-in - #33751
Conversation
|
Caution The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased. |
|
|
|
osv-scan is red from base drift, not this PR. It flags |
Greptile SummaryThis PR guards the destructive
Confidence Score: 5/5Safe to merge — changes are confined to the e2e test harness, add no new network calls, and the only behavioral shift (requiring an explicit opt-in before truncating spend logs) is strictly safer than the prior default-truncate behavior. The destructive truncate is now correctly double-gated: the guard in run_spend_log_cleanup requires opt_in == "1" AND e2e_test_ran, and the old sys.path mutation is gone. The implementation in e2e_db.py is a clean extraction of logic that was already proven correct in spend_e2e_client.py. No production code is touched. No files require special attention.
|
| Filename | Overview |
|---|---|
| tests/e2e/e2e_db.py | New helper module: run_spend_log_cleanup gates the destructive truncate on opt_in == "1" AND e2e_test_ran; reset_spend_logs moved here from spend_e2e_client.py unchanged |
| tests/e2e/conftest.py | pytest_sessionfinish simplified to a single call to run_spend_log_cleanup; sys.path mutation and dynamic import removed; no other hooks changed |
| tests/e2e/quota_management/spend_tracking/spend_e2e_client.py | reset_spend_logs and its unused os import removed from all and from the module; no other callers affected |
Reviews (4): Last reviewed commit: "test(e2e): guard destructive spend-log t..." | Re-trigger Greptile
Greptile SummaryThis PR guards the destructive
Confidence Score: 3/5Safe to merge for the spend-log guard itself; the unintended un-gating of The core opt-in guard is correct and well-tested. The main concern is in tests/e2e/conftest.py — specifically whether
|
| Filename | Overview |
|---|---|
| tests/e2e/conftest.py | Spend-log cleanup delegated to run_spend_log_cleanup; however removing the early return also un-gates bob_the_builder.remediate, which previously only ran when an e2e test had executed. |
| tests/e2e/e2e_db.py | New module housing the opt-in guard and the moved reset_spend_logs implementation; logic is correct but run_spend_log_cleanup returns True whether truncate() succeeded or failed. |
| tests/e2e/quota_management/spend_tracking/spend_e2e_client.py | Cleaned up: reset_spend_logs, its __all__ entry, and the now-unused os import removed; no other callers affected. |
| tests/e2e/test_e2e_db.py | New unit tests covering all opt-in/test-ran combinations and the best-effort exception swallow; no DB or proxy needed to run. |
Comments Outside Diff (1)
-
tests/e2e/conftest.py, line 120-131 (link)bob_the_builder.remediatenow runs unconditionallyThe old
pytest_sessionfinishopened withif not session.stash.get(_E2E_TEST_RAN, False): return, which guarded both the spend-log cleanup and the subsequentbob_the_builder.remediate(session)call. The refactor moved the guard intorun_spend_log_cleanup, but the earlyreturnwas removed entirely, soremediateis now invoked at the end of every session — including pure unit runs (pytest tests/e2e/test_e2e_db.py) where noe2e-marked test ever ran. Ifremediatehas side effects when called on an empty session (filing tickets, posting results, triggering CI actions), those will now fire on every harness-unit run. The PR description does not mention this as an intentional change.
Reviews (1): Last reviewed commit: "test(e2e): guard destructive spend-log t..." | Re-trigger Greptile
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
|
Addressed the P1 in 0f1d45f. You're right that dropping the early return un-gated @greptileai please review the current head 0f1d45f |
|
The |
…t-in tests/e2e/conftest.py's pytest_sessionfinish truncated LiteLLM_SpendLogs against whatever DATABASE_URL resolved to, gated only by "an e2e test body ran". Pointed at a shared or staging DB, a routine local run wiped real spend data. It also reached the truncate helper through a sys.path.insert into quota_management/spend_tracking/spend_e2e_client.py, a cross-suite import-by-path hack it then unwound in a finally. The cleanup now routes through a new run_spend_log_cleanup in a top-level tests/e2e/e2e_db.py, which fires the destructive truncate only when the operator set E2E_RESET_SPEND_LOGS=1 and an e2e test actually ran. Any other value (unset, 0, true, empty) leaves the DB untouched, so presence of the variable alone or a test run alone never arms the truncate. The decision plus the injectable truncate callable live in that pure helper, and conftest is a thin adapter that supplies os.environ.get(...), the session stash, and reset_spend_logs. reset_spend_logs itself moved from spend_e2e_client.py into e2e_db.py (implementation unchanged), sitting next to e2e_config and lifecycle so both conftest and any suite import it by name; the sys.path munging is gone. Nothing else imported reset_spend_logs, so spend_e2e_client.py drops the definition, its __all__ entry, and the now-unused os import.
cf04467 to
a2636fa
Compare
|
@greptileai please review the current head a2636fa — the branch was rebased onto current staging and the spend-log truncate guard was reapplied as a single commit; the prior review is pinned to a now-rewritten commit |
Relevant issues
Linear ticket
Resolves LIT-4555
Pre-Submission checklist
Please complete all items before asking a LiteLLM maintainer to review your PR
@greptileaito re-request a review after pushing changes)Screenshots / Proof of Fix
Harness-internal change with no live-proxy HTTP surface, so the proof drives the exact production functions the session-finish hook calls (
run_spend_log_cleanupandreset_spend_logs) against a real Postgres seeded with spend rows. No mocks; the truncate hits realLiteLLM_SpendLogsrowsThe middle case is the bug this closes: a routine local run that merely exercised a test, pointed at a shared or staging
DATABASE_URL, no longer wipes real spend data. The truncate now happens only on the explicit opt-inHarness gates, all green on this branch:
Type
🧹 Refactoring
Changes
tests/e2e/conftest.py'spytest_sessionfinishtruncatedLiteLLM_SpendLogsagainst whateverDATABASE_URLresolved to, gated only by "an e2e test body ran". Pointed at a shared or staging DB, a routine local run wiped real spend data. It also reached the truncate helper through asys.path.insertintoquota_management/spend_tracking/spend_e2e_client.py, a cross-suite import-by-path hack it then unwound in afinallyThe cleanup now routes through a new
run_spend_log_cleanupin a top-leveltests/e2e/e2e_db.py, which fires the destructive truncate only when the operator setE2E_RESET_SPEND_LOGS=1and an e2e test actually ran. Any other value (unset,0,true, empty) leaves the DB untouched, so presence of the variable alone or a test run alone never arms the truncate. The decision plus the injectabletruncatecallable live in that pure helper, andconftestis a thin adapter that suppliesos.environ.get(...), the session stash, andreset_spend_logsreset_spend_logsitself moved fromspend_e2e_client.pyintoe2e_db.py(implementation unchanged: sameTRUNCATE TABLE "LiteLLM_SpendLogs", sameDATABASE_URLdefault). It now sits next toe2e_configandlifecycle, so both the top-levelconftestand any suite import it by name; thesys.pathmunging is gone. Nothing else in the repo importedreset_spend_logs, sospend_e2e_client.pydrops the definition, its__all__entry, and the now-unusedosimport with no other caller affectedQA runbook
This PR edits
tests/e2ebut adds noe2e-marked tests, and the only behavior change is in the session-finish cleanup, which has no request surface. So there is no live-proxy reproduction. To verify the guard by hand against a local Postgres holding spend rows:LiteLLM_SpendLogswith a few rows on the DB yourDATABASE_URLpoints atE2E_RESET_SPEND_LOGSunset, run an e2e session that touches a live proxy (pytest -m e2e tests/e2e/quota_management/spend_tracking/) and confirm the seeded rows are still present afterwardE2E_RESET_SPEND_LOGS=1, run the same session, and confirmLiteLLM_SpendLogsis empty afterwardFinal Attestation
Link to Devin session: https://app.devin.ai/sessions/2b24353a94a44e6998e91f09c67fa117
Requested by: @yassin-berriai